-module(cus_http).
-behaviour(gen_server).

-include("cus.hrl").
-include("logger.hrl").
-include("cus_pb.hrl").

-export([
    start_link/0,
    init/1,
    handle_call/3,
    handle_cast/2,
    handle_info/2,
    terminate/2,
    code_change/3,
    stop/0
        ]).


-define(HTTP_PORT, 5380).



%% 增加黑名单
process("add_black") ->
    P = get(params),
    Uid = ?param("uid", P),
    QuanId = ?param("quan_id", P),
    BlackUid = ?param("black_uid", P),
    cus_user:add_black(Uid, QuanId, BlackUid),
    {true, []};
    
%% 取消黑名单
process("del_black") ->
    P = get(params),
    Uid = ?param("uid", P),
    QuanId = ?param("quan_id", P),
    BlackUid = ?param("black_uid", P),
    cus_user:del_black(Uid, QuanId, BlackUid),
    {true, []};
    
%% 关注会话
process("add_follow") ->
    P = get(params),
    Uid = ?param("uid", P),
    Cid = ?param("cid", P),
    cus_user:add_follow(Uid, Cid),
    {true, []};
    
%% 取消关注会话
process("del_follow") ->
    P = get(params),
    Uid = ?param("uid", P),
    Cid = ?param("cid", P),
    cus_user:del_follow(Uid, Cid),
    {true, []};

%% 置顶会话
process("add_stick") ->
    P = get(params),
    Uid = ?param("uid", P),
    Cid = ?param("cid", P),
    cus_user:add_stick(Uid, Cid),
    {true, []};

%% 取消置顶
process("del_stick") ->
    P = get(params),
    Uid = ?param("uid", P),
    Cid = ?param("cid", P),
    cus_user:del_stick(Uid, Cid),
    {true, []};

%% 设置会话免打扰
process("add_muteNotofication") ->
    P = get(params),
    Uid = ?param("uid", P),
    Cid = ?param("cid", P),
    UserInfo = cus_user:add_muteNotofication(Uid, Cid),
    PushUser = cus_do:push_user(UserInfo),
    mnesia:dirty_write(cus_push_user_tab, PushUser),
    {true, []};

%% 取消会话免打扰
process("del_muteNotofication") ->
    P = get(params),
    Uid = ?param("uid", P),
    Cid = ?param("cid", P),
    UserInfo = cus_user:del_muteNotofication(Uid, Cid),
    PushUser = cus_do:push_user(UserInfo),
    mnesia:dirty_write(cus_push_user_tab, PushUser),
    {true, []};

%% 设置全局免打扰
process("add_global_muteNotofication") ->
    P = get(params),
    Uid = ?param("uid", P),
    UserInfo = cus_user:add_global_muteNotofication(Uid),
    PushUser = cus_do:push_user(UserInfo),
    mnesia:dirty_write(cus_push_user_tab, PushUser),
    {true, []};

%% 取消全局免打扰
process("del_global_muteNotofication") ->
    P = get(params),
    Uid = ?param("uid", P),
    UserInfo = cus_user:del_global_muteNotofication(Uid),
    PushUser = cus_do:push_user(UserInfo),
    mnesia:dirty_write(cus_push_user_tab, PushUser),
    {true, []};

%% 设置推送内容拦截
process("add_pushContent") ->
    P = get(params),
    Uid = ?param("uid", P),
    UserInfo = cus_user:add_pushContent(Uid),
    PushUser = cus_do:push_user(UserInfo),
    mnesia:dirty_write(cus_push_user_tab, PushUser),
    {true, []};

%% 取消推送内容拦截
process("del_pushContent") ->
    P = get(params),
    Uid = ?param("uid", P),
    UserInfo = cus_user:del_pushContent(Uid),
    PushUser = cus_do:push_user(UserInfo),
    mnesia:dirty_write(cus_push_user_tab, PushUser),
    {true, []};    

%% 获取系统表情包
process("get_sys_emotion_packets") ->
    PacketList = cus_redis:get_sys_emotion_packets(),
    Keys = [id, name, display_order, directory, available, cover, code, detail],
    Fun = fun(Packet, Acc) ->
                  case Packet#cus_sys_emotion_packet.available of
                      "0" ->
                          Acc;
                      Available ->
                          [{obj, lists:zip(Keys, [?l2b(Packet#cus_sys_emotion_packet.id),
                                                  ?l2b(Packet#cus_sys_emotion_packet.name),
                                                  ?l2b(Packet#cus_sys_emotion_packet.displayOrder),
                                                  ?l2b(Packet#cus_sys_emotion_packet.directory),
                                                  ?l2b(Available),
                                                  ?l2b(Packet#cus_sys_emotion_packet.cover),
                                                  ?l2b(Packet#cus_sys_emotion_packet.code),
                                                  ?l2b(Packet#cus_sys_emotion_packet.detail)])} | Acc]
                  end
          end,
    {true, lists:foldl(Fun, [], PacketList)};

%% 获取系统表情
process("get_sys_emotion") ->
    P = get(params),
    PacketId = ?param("packet_id", P),
    Packet = cus_redis:get_sys_emotion_packet(PacketId),
    EmotionList = Packet#cus_sys_emotion_packet.emotions,
    Keys = [packet_id, name, display_order, file_name, code, available, url, url_jpg],
    Fun = fun(Emotion, Acc) ->
                  case Emotion#cus_sys_emotion.available of
                      "0" ->
                          Acc;
                      Available ->
                          [{obj, lists:zip(Keys, [?l2b(PacketId),
                                                  ?l2b(Emotion#cus_sys_emotion.name),
                                                  ?l2b(Emotion#cus_sys_emotion.displayOrder),
                                                  ?l2b(Emotion#cus_sys_emotion.fileName),
                                                  ?l2b(Emotion#cus_sys_emotion.code),
                                                  ?l2b(Available),
                                                  ?l2b(Emotion#cus_sys_emotion.url),
                                                  ?l2b(Emotion#cus_sys_emotion.urlJpg)])} | Acc]
                  end
          end,
    {true, lists:foldl(Fun, [], EmotionList)};

%% 添加自定义表情
process("add_user_emotion") ->
    P = get(params),
    Uid = ?param("uid", P),
    Url = ?param("url", P),
    JPGUrl = ?param("jpg_url", P),
    Width = ?param("width", P),
    Height = ?param("height", P),
    Result = cus_user:add_user_emotion(Uid, Url, JPGUrl, Width, Height),
    case Result of
        "success" ->
            {true, []};
        _ ->
            ?INFO_MSG("add_user_emotion failed, Uid : ~p, Url : ~p, JPGUrl : ~p, Result : ~p", [Uid, Url, JPGUrl, Result]),
            {false, [{obj, [{msg, ?l2b(Result)}]}]}
    end;
        
%% 删除自定义表情
process("del_user_emotion") ->
    P = get(params),
    Uid = ?param("uid", P),
    Urls = ?param("urls", P),
    UrlList = string:token(Urls, ","),
    cus_user:del_user_emotion(Uid, UrlList),
    {true, []};

%% 获取自定义表情
process("get_user_emotions") ->
    P = get(params),
    Uid = ?param("uid", P),
    Result = cus_user:get_user_emotions(Uid),
    Keys = [url, jpg_url, width, height],
    Data = [{obj, lists:zip(Keys, [?l2b(X) || X <- tuple_to_list(Item)])} || Item <- Result],
    {true, Data};

%% 上传最近使用的表情
process("upload_lately_emotions") ->
    P = get(params),
    Uid = ?param("uid", P),
    Packet = ?param("packet", P),
    Data = [begin
                {ok, Url} = rfc4627:get_field(Item, "url"),
                {ok, JPGUrl} = rfc4627:get_field(Item, "jpg_url"),
                {ok, Type} = rfc4627:get_field(Item, "emotion_type"), %%0:自定义,1:官方
                {ok, Time} = rfc4627:get_field(Item, "last_time"),
                {?b2l(Url), ?b2l(JPGUrl), ?b2l(Type), ?b2l(Time)}
            end || Item <- Packet],
    cus_user:upload_lately_emotions(Uid, Data), 
    {true, []};

%% 获取最近使用的表情
process("get_lately_emotions") ->
    P = get(params),
    Uid = ?param("uid", P),
    Result = cus_user:get_lately_emotions(Uid),
    Result1 = lists:sublist(Result, 16),
    Keys = [url, jpg_url, emotion_type, last_time],
    Data = [{obj, lists:zip(Keys, [?l2b(X) || X <- tuple_to_list(Item)])} || Item <- Result1],
    {true, Data};

%% 会话信息
process("conversation_info") ->
    P = get(params),
    Cid = ?param("cid", P),
    Data = case cus_conversation:conversation_info(Cid) of
               undefined ->
                   [];
               Info ->
                   List = [{"cid", ?l2b(Cid)},
                           {"name", ?l2b(Info#cus_conversation.name)},
                           {"members", ?l2b(string:join(Info#cus_conversation.members, ","))},
                           {"status", Info#cus_conversation.status},
                           {"updateTime", ?l2b(Info#cus_conversation.updateTime)}],
                   [{obj, List}]
           end,
    {true, Data};

%% 创建会话
process("create_conversation") ->
    P = get(params),
    Cid = ?param("cid", P),
    Name = ?param("name", P),
    Members = ?param("members", P),
    Text = ?param("text", P),
    MemberList = string:tokens(Members, ","),
    Info = #cus_conversation{cid = Cid,
                             name = Name,
                             members = MemberList},
    cus_conversation:create_conversation(Cid, Info),
    %% 给当前会话成员发送一条加入会话的消息
    Msg = cus_sys_msg:prompt(Cid, Text, ""),
    spawn(fun() -> cus_do:do_message(true, Msg#msg.fromuser, Msg) end),
    {true, []};

%% 解散会话
process("disband_conversation") ->
    P = get(params),
    Cid = ?param("cid", P),
    Text = ?param("text", P),
    cus_conversation:disband_conversation(Cid),
    %% 给当前会话成员发送一条解散会话的消息
    Msg = cus_sys_msg:prompt(Cid, Text, ""),
    spawn(fun() -> cus_do:do_message(true, Msg#msg.fromuser, Msg) end),
    {true, []};

%% 用户加入会话
process("user_add_to_conversation") ->
    P = get(params),
    Cid = ?param("cid", P), 
    Uids = ?param("uids", P),
    [begin
         {ok, Uid} = rfc4627:get_field(Item, "uid"),
         {ok, Text} = rfc4627:get_field(Item, "text"),
         cus_conversation:add_member(Cid, ?b2l(Uid)),
         Msg = cus_sys_msg:prompt(Cid, ?b2l(Text), ""),
         spawn(fun() -> cus_do:do_message(true, Msg#msg.fromuser, Msg) end)
     end || Item <- Uids],
    {true, []};
    
%% 用户退出会话
process("user_exit_to_conversation") ->
    P = get(params),
    Cid = ?param("cid", P),
    Uid = ?param("uid", P),
    Text = ?param("text", P),
    cus_conversation:del_member(Cid, Uid),
    Msg = cus_sys_msg:prompt(Cid, Text, ""),
    spawn(fun() -> cus_do:do_message(true, Msg#msg.fromuser, Msg) end),
    {true, []};

%% 变更会话名称
process("change_conversation_name") ->
    P = get(params),
    Cid = ?param("cid", P),
    Name = ?param("name", P),
    Text = ?param("text", P),
    cus_conversation:change_name(Cid, Name),
    Msg = cus_sys_msg:prompt(Cid, Text, ""),
    spawn(fun() -> cus_do:do_message(true, Msg#msg.fromuser, Msg) end),
    {true, []};

%% 查询用户token
process("get_user_token") ->
    P = get(params),
    Uid = ?param("uid", P),
    Token = cus_token_redis:get_user_token(Uid),
    {true, ?l2b(Token)};

process("update_device_token") ->
    P = get(params),
    Uid = ?param("uid", P),
    DeviceToken = ?param("deviceToken", P),
    Token = cus_user:update_device_token(Uid, DeviceToken),
    {true, ?l2b(Token)};

%% 查询会话消息
process("get_conversation_message") ->
    P = get(params),
    Uid = ?param("uid", P),
    Cid = ?param("cid", P),
    Mid = ?param("mid", P),
    Size = ?param("size", P),
    Messages = case Mid of
                   "" ->
                       cus_user:get_conversation_message(Uid, Cid, ?l2i(Size));
                   _ ->
                       List = re:split(Mid, "_", [{return, list}]),
                       Seq = lists:last(List),
                       cus_user:get_conversation_message(Uid, Cid, ?l2i(Seq), ?l2i(Size))
               end,
    Keys = [mid, conversationid, msgtype, fromuser, members, content, msgtime, msgstatus],
    Vs = [begin
              Mid1 = Message#msg.mid,
              Cid = Message#msg.conversationid,
              MsgType = Message#msg.msgtype,
              FromUser = Message#msg.fromuser,
              Members = Message#msg.members,
              Content = Message#msg.content,
              MsgTime = Message#msg.msgtime,
              MsgStatus = Message#msg.msgstatus,
              [?l2b(Mid1), ?l2b(Cid), ?l2b(MsgType), ?l2b(FromUser), ?l2b(Members), ?l2b(Content), ?l2b(MsgTime), ?l2b(MsgStatus)]
          end || Message <- Messages],
    Data = [{obj, lists:zip(Keys, V)} || V <- Vs],
    {true, Data};

%% 撤销消息
process("revoke_message") ->
    P = get(params),
    Uid = ?param("uid", P),
    Mid = ?param("mid", P),
    Text = ?param("text", P),
    Result = cus_user:revoke_message(Uid, Mid),
    case Result of
        "success" ->
            Msg = cus_sys_msg:revoke_message(Mid, Text),
            spawn(fun() -> cus_do:do_message(true, Msg#msg.fromuser, Msg) end),
            {true, []};
        Code ->
            {false, {obj, [{failed_code, ?l2b(Code)}]}}
    end;
    
process("logout") ->
    P = get(params),
    Uid = ?param("uid", P),
    [begin
         {_Sid, Pid} = element(2, Session),
         gen_server:cast(Pid, closed)
     end || Session <- ejabberd_sm:get_user_sessions(?l2b(Uid), ?SERVER)],
    cus_user:logout(Uid),
    {true, []};

process("get_k3_token") ->
    P = get(params),
    Sign = ?param("sign", P),
    GmtStr = cus_util:gmt_str(),
    Sign1 = re:replace(Sign, "\\[date\\]", GmtStr, [{return, list}]),
    [_Domain, _Bucket, Ak, Sk] = cus_config:k3(),
    Cipher = base64:encode(cus_util:hmac_sha(Sign1, Sk)),
    Token = <<"KSS ", Ak/binary, ":", Cipher/binary>>,
    Data = [{obj, [{token, Token}, {gmtTime, ?l2b(GmtStr)}]}],
    {true, Data};

process("get_connect_info") ->
    [EncryptFlag, _PrivateKey] = cus_config:connect_encrypt(),
    [Domain, Bucket, _Ak, _Sk] = cus_config:k3(),
    Data = [{obj, [{encrypt, EncryptFlag},
                   {k3_domain, Domain},
                   {k3_bucket, Bucket}]}],
    {true, Data};

process("clear_device_token_badge") ->
    P = get(params),
    DeviceToken = ?param("deviceToken", P),
    Count = ?param("count", P),   %% 0代表清除所有
    cus_push:clear_badge(DeviceToken, ?l2i(Count)),
    {true, []};

process("send_sys_prompt") ->
    P = get(params),
    Text = ?param("text", P),
    Members = ?param("members", P),
    Cid = ?param("cid", P),
    Msg = cus_sys_msg:prompt(Cid, Text, Members),
    spawn(fun() -> cus_do:do_message(true, Msg#msg.fromuser, Msg) end),
    {true, []};

process("send_test_msg") ->
    P = get(params),
    Uid = ?param("uid", P),
    Name = ?param("name", P),
    Icon = ?param("icon", P),
    Cid = ?param("cid", P),
    Num = ?param("num", P),
    Json = {obj,[{text, <<"this is a test msg">>}, 
                 {fromUserName, ?l2b(Name)},
                 {fromUserIcon, ?l2b(Icon)}]},
    J = rfc4627:encode(Json),
    Msg = cus_sys_msg:new_message(Cid, "text", Uid, J, ""),
    [spawn(fun() -> cus_do:do_message(true, Msg#msg.fromuser, Msg) end)
     || _X <- lists:seq(1, ?l2i(Num))],
    {true, []};

process("push") ->
    P = get(params),
    Uids = ?param("uids", P),
    Alert = ?param("alert", P),
    Extra = ?param("extra", P),
    FoldFun = fun({obj, [{Key, List}]}, Acc) when is_list(List) ->
                      [{Key, list_to_binary(List)} | Acc];
                  ({obj, [{Key, List}]}, Acc) ->
                      [{Key, List} | Acc];
                  (_Other, Acc) ->
                      Acc
              end,
    Extra1 = lists:foldl(FoldFun, [], Extra),
    UidList = string:tokens(Uids, ","),
    cus_push:push(UidList, Alert, Extra1),
    {true, []};

process("cornucopia_finish") ->
    P = get(params),
    Members = ?param("members", P),
    Cid = ?param("cid", P),
    Text = ?param("text", P),
    LotteryId = ?param("lotteryId", P),
    Msg = cus_sys_msg:cornucopia_finish(Cid, Text, LotteryId, Members),
    spawn(fun() -> cus_do:do_message(true, Msg#msg.fromuser, Msg) end),
    {true, []};

process(_Method) ->
    {false, "method is not undefined!"}.


start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).


stop() ->
    exit(erlang:whereis(?MODULE), kill).


init([]) ->
    misultin:start_link([{port, ?HTTP_PORT},
                         {acceptors_poolsize, 500},
                         {max_connections, 65535},
                         {loop, fun(Req) -> handle_http(Req) end}]),
    ?INFO_MSG("cus http service start on ~p", [?HTTP_PORT]),
    {ok, {}}.


handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.


handle_cast(_Msg, State) ->
    {noreply, State}.


handle_info(_Info, State) ->
    {noreply, State}.


terminate(_Reason, _State) ->
    ok.


code_change(_OldVsn, State, _Extra) ->
    {ok, State}.


handle_http(Req) ->
    try
        Args = http_args(Req),
        if
            Args == [] ->
                Req:ok([{"Content-Type", "text/xml"}], "~s", [cus_api:api()]);
            true ->
                P = ?param("p", Args),
                {ok, {obj, BodyJson}, _} = rfc4627:decode(P),
                Args2 = lists:foldl(fun({K, V}, Acc)->  [{K, ?list(V)} | Acc] end, [], BodyJson),
                put(params, Args2),
                Method = ?param("method", Args2),
                {Success, Entity} = process(Method),
                http_response(Success, Entity, Req)
        end
    catch
        _C:Reason ->
            ?ERROR_MSG("cus http error, reason : ~p~n, args : ~p~n, trace:~p~n", [Reason, http_args(Req), erlang:get_stacktrace()]),
            http_response(false, list_to_binary("unknow error!"), Req)
    end.


http_args(Req) ->
    Method = Req:get(method),
    case Method of
        'GET' ->
            Req:parse_qs();
        'POST' ->
            Req:parse_qs() ++ Req:parse_post();
        _ ->
            []
    end.


http_response(Success, Entity, Req) ->
    Res = {obj,[{success, Success}, {data, Entity}]},
    J = rfc4627:encode(Res),
    Req:ok([{"Content-Type", "text/json"}], "~s", [J]).
